/****************************************************************
 * Filename: busDemo.c
 * -------------------------------
 * Copyright(C),
 * Author: zhenquan.qiu
 * Version: V1.0.0
 * Last modified: 01/03 2017 10:19
 * Description:
 *
 * Change Log:
 * NO.	Author		    Date		Modified
 * 00	zhenquan.qiu	01/03 2017
 ****************************************************************/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>

#include "debugk.h"
#include "busDemo.h"


#define VERSION "$REVISION: 1.9 $"

static unsigned int gDebugMask = LOG_ERROR;


static int busdemo_match(struct device *dev, struct device_driver *drv)
{
    struct busdemo_device *_busdemo_device = to_busdemo_device(dev);

    FUNC_ENTER();

    return !strncmp(_busdemo_device->name, drv->name, strlen(drv->name));
}

static int busdemo_uevent(struct device *dev, struct kobj_uevent_env *env)
{
    FUNC_ENTER();

    if (add_uevent_var(env, "BUSDEMO_VERSION=%s", VERSION))
        return -ENOMEM;

    return 0;
}

static void busdemo_release(struct device *dev)
{
    FUNC_ENTER();
}

static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
    FUNC_ENTER();
    return snprintf(buf, PAGE_SIZE, "%s\n", VERSION);
}

static struct bus_type busdemo_bus_type = {
    .name   = "busdemo",
    .match  = busdemo_match,
    .uevent = busdemo_uevent,
};

static struct device busdemo_bus = {
    .init_name = "busdemo0",
    .release   = busdemo_release,
};

static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);

static int __init busdemo_init(void)
{
    int ret;

    FUNC_ENTER();

    ret = bus_register(&busdemo_bus_type);
    if (ret) {
        pr_edbg("register bus faild");
        return ret;
    }

    ret = bus_create_file(&busdemo_bus_type, &bus_attr_version);
    if (ret) {
        pr_edbg("create dir faild in sysfs");
        goto err_bus_register;
    }

    ret = device_register(&busdemo_bus);
    if (ret) {
        pr_edbg("register device faild");
        goto err_bus_register;
    }

    return 0;

err_bus_register:
    bus_unregister(&busdemo_bus_type);
    return ret;
}

static void busdemo_exit(void)
{
    FUNC_ENTER();

    device_unregister(&busdemo_bus);
    bus_unregister(&busdemo_bus_type);
}

module_init(busdemo_init);
module_exit(busdemo_exit);

static void busdemo_dev_release(struct device *dev)
{
    FUNC_ENTER();

}

int register_busdemo_device(struct busdemo_device *busdemo_dev)
{
    busdemo_dev->device.bus = &busdemo_bus_type;
    busdemo_dev->device.parent = &busdemo_bus;
    busdemo_dev->device.init_name = busdemo_dev->name;
    busdemo_dev->device.release = busdemo_dev_release;

    return device_register(&busdemo_dev->device);
}

void unregister_busdemo_device(struct busdemo_device *busdemo_dev)
{
    FUNC_ENTER();

    device_unregister(&busdemo_dev->device);
}

EXPORT_SYMBOL(register_busdemo_device);
EXPORT_SYMBOL(unregister_busdemo_device);

static int busdemo_drv_probe(struct device *_dev)
{
    struct busdemo_driver *drv;

    FUNC_ENTER();

    drv = to_busdemo_driver(_dev->driver);

    return drv->probe(to_busdemo_device(_dev));
}

static int busdemo_drv_remove(struct device *_dev)
{
    struct busdemo_driver *drv;

    FUNC_ENTER();

    drv = to_busdemo_driver(_dev->driver);

    return drv->remove(to_busdemo_device(_dev));
}

static ssize_t show_version(struct device_driver *drv, char *buf)
{
    struct busdemo_driver *busdemo_drv;

    FUNC_ENTER();

    busdemo_drv = to_busdemo_driver(drv);

    sprintf(buf, "%s\n", busdemo_drv->version);

    return strlen(buf);
}

int register_busdemo_driver(struct busdemo_driver *drv)
{
    int ret;

    drv->driver.bus = &busdemo_bus_type;

    if (drv->probe)
        drv->driver.probe = busdemo_drv_probe;
    if (drv->remove)
        drv->driver.remove = busdemo_drv_remove;

    ret = driver_register(&drv->driver);
    if (ret)
        return ret;

    drv->version_attr.attr.name = "version";
    drv->version_attr.attr.mode = S_IRUGO;
    drv->version_attr.show      = show_version;
    drv->version_attr.store     = NULL;

    return driver_create_file(&drv->driver, &drv->version_attr);
}

void unregister_busdemo_driver(struct busdemo_driver *drv)
{
    FUNC_ENTER();

    driver_unregister(&drv->driver);
}

EXPORT_SYMBOL(register_busdemo_driver);
EXPORT_SYMBOL(unregister_busdemo_driver);

MODULE_AUTHOR("gnsyxiang <gnsyxiang@163.com>");
MODULE_LICENSE("Dual BSD/GPL");

